package org.hackystat.ui.sensordataviewer.client;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.TreeItem;

/**
 * This class represents one event of sensor data elements in the SDViewer display, an event is
 * quanitized to the the sensor data types occuring within one minute.
 * @author David J. Nickles
 */
public class Event extends TreeItem {
  
  /** {@literal} NEW_ODD_ROW style. */        
  private static final String NEW_ODD_ROW = "gwt-TreeItem-new-odd"; 
  /** {@literal} NEW_EVEN_ROW style. */        
  private static final String NEW_EVEN_ROW = "gwt-TreeItem-new-even"; 
  /** {@literal} OUTER_ODD_ROW style. */        
  private static final String OUTER_ODD_ROW = "gwt-TreeItem-odd-outer"; 
  /** {@literal} INNER_ODD_ROW style. */        
  private static final String INNER_ODD_ROW = "gwt-TreeItem-odd-inner"; 
  /** {@literal} EVEN_ROW style. */        
  private static final String EVEN_ROW = "gwt-TreeItem-even"; 

  private String header;
  private long eventTstamp;
  private int count;
  private int modCount;
  private int rootOddOrEven;
  private int lastChildOddOrEven;
  private boolean inHrefState;
  private int beginHrefStateIndex;
  private Image rpcStatusImage;
  private HTML text;
  private HTML statusCount;
  private CompositeWidget master;
  private boolean memoryOpen;
  private boolean memoryFireEvents;
  private Date lastTstamp;
  private ArrayList cache;
  private boolean introState;
  
  /**
   * Makes a new SDVEvent containing this item.
   * @param list of sensor data elements
   * @param oddoreven if the parent is odd or even for coloring
   * @param widget the 
   * 
   * @gwt.typeArgs list <java.util.String>
   */
  public Event(ArrayList list, int oddoreven, CompositeWidget widget) {
    this.master = widget;
    this.introState = true;
    HorizontalPanel row1 = new HorizontalPanel();
    this.rpcStatusImage = new Image(CompositeWidget.WAIT_IMAGE);
    this.rpcStatusImage.setPixelSize(15, 15);
    this.rpcStatusImage.setVisible(false);
    row1.add(this.rpcStatusImage);
    this.header = (String)list.get(0);
    this.count = Integer.parseInt((String)list.get(1));
    this.eventTstamp = Long.parseLong((String)list.get(2));
    text = new HTML("<b>" + this.header + " [" + this.count + '/' + this.count + "]</b>");
    text.setStyleName("gwt-TreeItem");
    row1.add(text);
    this.setWidget(row1);
    this.count = 0;
    this.modCount = 0;
    this.beginHrefStateIndex = 0;
    this.inHrefState = true;
    this.rootOddOrEven = oddoreven;
    if ((this.rootOddOrEven % 2) == 0) {
      addStyleName("gwt-TreeItem-new-odd");
    } 
    else {
      addStyleName("gwt-TreeItem-new-even");
    }
    Iterator it = list.listIterator(3);
    String href = null;
    while (it.hasNext()) {
      count++;
      href = (String)it.next();
      TreeItem leaf = new TreeItem(href);
      addItem(leaf);
      if ((++oddoreven) % 2 == 0) {
        leaf.addStyleName(NEW_ODD_ROW);
      } 
      else {
        leaf.addStyleName(NEW_EVEN_ROW);
      }
    }
    String lastXmlTstamp = href.substring(href.lastIndexOf('/') + 1, href.length() - 1);
    this.lastTstamp = DateTimeFactories.getInstance().getZonefactory().parse(lastXmlTstamp);
    this.lastChildOddOrEven = oddoreven;
  }
  
  /**
   * Gets the count of new elements.
   * @return the count of new elements.
   */
  public int size() {
    return this.count;
  }
  
  /**
   * Receive the list of sensor data and insert in place of the hrefs.
   * @param list of sensor data instances as string arrays.
   * @gwt.typeArgs list <java.util.ArrayList<java.lang.String>>
   */
  public void insert(ArrayList list) {
    for (int i = this.beginHrefStateIndex, j = 0; j < list.size(); ++i, ++j) {
      /** @gwt.typeArgs innerList <java.lang.String> */
      ArrayList innerList = (ArrayList)list.get(j);
      this.getChild(i).setText((String)innerList.get(0));
      Iterator it = innerList.listIterator(1);
      while (it.hasNext()) {
        this.getChild(i).addItem((String)it.next());
      }
    }
    this.inHrefState = false;
    this.beginHrefStateIndex = this.getChildCount();
    this.rpcStatusImage.setVisible(false);
    super.setState(this.memoryOpen, this.memoryFireEvents);
  }
  
  /**
   * Merges the two events.
   * @param e another event.
   */
  public void merge(Event e) {
    this.modCount = 0;
    TreeItem leaf1 = null;
    for (int i = 0; i < e.getChildCount(); ++i) {
      leaf1 = new TreeItem(e.getChild(i).getText());
      addItem(leaf1);
      ++this.modCount;
      if ((++this.lastChildOddOrEven % 2) == 0) {
        leaf1.addStyleName(NEW_ODD_ROW);
      } 
      else {
        leaf1.addStyleName(NEW_EVEN_ROW);
      }
    }
    String href = leaf1.getText();
    String lastXmlTstamp = href.substring(href.lastIndexOf('/') + 1, href.length() - 1);
    this.lastTstamp = DateTimeFactories.getInstance().getZonefactory().parse(lastXmlTstamp);
    this.text.setHTML("<b>" + this.header + " [" + this.modCount + '/' 
      + (this.count += this.modCount) + "]</b>");
    this.inHrefState = true;
    setState(getState());
    //this.rpcStatusImage.setVisible(false);
  }

  /**
   * This method sets the TreeItem state.
   * @param open true if the tree branch is to be expanded, false if collapsed.
   * @param fireEvents true if subtrees should be notified.
   */
  public void setState(boolean open, boolean fireEvents) {
    // if has opened before then return and do nothing (you have already loaded the children)
    if (this.inHrefState && open && this.getChildCount() > 0) {
      ArrayList hrefList = new ArrayList();
      this.rpcStatusImage.setVisible(open);
      for (int i = this.beginHrefStateIndex, n = this.getChildCount(); i < n; ++i) {
        hrefList.add(((TreeItem) this.getChild(i)).getText());
      }
      master.rpcGetHrefs(hrefList, this);
      this.memoryOpen = open;
      this.memoryFireEvents = fireEvents;
    } 
    else {
      super.setState(open, fireEvents);
    }
  }

  /**
   * Gets the timestamp of the last item in this event.
   * @return Date object that is the last timestamp that occurs in this event.
   */
  public Date getLastTstamp() {
    return this.lastTstamp;
  }

  /**
   * Gets the quantized timestamp of this event.
   * @return Date object that is the timestamp of this event rounded down to the minute.
   */
  public long getEventTstamp() {
    return this.eventTstamp;
  }

  /**
   * Gets the String displayed in the header of this tree item.
   * @return String that represents this tree item.
   */
  public String getHeader() {
    return this.text.getText();
  }

  /**
   * Removes the new display styles from the items in this branch.
   */
  public void age() {
    this.text.setHTML(this.header + " [0/" + this.count + ']');
    if ((this.rootOddOrEven % 2) == 0) {
      removeStyleName(NEW_ODD_ROW);
      addStyleName(OUTER_ODD_ROW);
    } 
    else {
      removeStyleName(NEW_EVEN_ROW);
      addStyleName(EVEN_ROW);
    }
    text.setText(text.getText());
    for (int i = 0, n = this.getChildCount(), oe = this.rootOddOrEven; i < n; ++i) {
      if ((++oe % 2) == 0) {
        getChild(i).removeStyleName(NEW_ODD_ROW);
        getChild(i).addStyleName(INNER_ODD_ROW);
      } 
      else {
        getChild(i).removeStyleName(NEW_EVEN_ROW);
        getChild(i).addStyleName(EVEN_ROW);
      }
    }
  }
}